package gssapiimport ()// RFC 4121, section 4.2.6.2const (// HdrLen is the length of the Wrap Token's headerHdrLen = 16// FillerByte is a filler in the WrapToken structureFillerBytebyte = 0xFF)// WrapToken represents a GSS API Wrap token, as defined in RFC 4121.// It contains the header fields, the payload and the checksum, and provides// the logic for converting to/from bytes plus computing and verifying checksumstypeWrapTokenstruct {// const GSS Token ID: 0x0504 Flags byte// contains three flags: acceptor, sealed, acceptor subkey// const Filler: 0xFF EC uint16// checksum length. big-endian RRC uint16// right rotation count. big-endian SndSeqNum uint64// sender's sequence number. big-endian Payload []byte// your data! :) CheckSum []byte// authenticated checksum of { payload | header }}// Return the 2 bytes identifying a GSS API Wrap tokenfunc getGssWrapTokenId() *[2]byte {return &[2]byte{0x05, 0x04}}// Marshal the WrapToken into a byte slice.// The payload should have been set and the checksum computed, otherwise an error is returned.func ( *WrapToken) () ([]byte, error) {if .CheckSum == nil {returnnil, errors.New("checksum has not been set") }if .Payload == nil {returnnil, errors.New("payload has not been set") } := HdrLen// Offset of the payload in the token := HdrLen + len(.Payload) // Offset of the checksum in the token := make([]byte, +int(.EC))copy([0:], getGssWrapTokenId()[:]) [2] = .Flags [3] = FillerBytebinary.BigEndian.PutUint16([4:6], .EC)binary.BigEndian.PutUint16([6:8], .RRC)binary.BigEndian.PutUint64([8:16], .SndSeqNum)copy([:], .Payload)copy([:], .CheckSum)return , nil}// SetCheckSum uses the passed encryption key and key usage to compute the checksum over the payload and// the header, and sets the CheckSum field of this WrapToken.// If the payload has not been set or the checksum has already been set, an error is returned.func ( *WrapToken) ( types.EncryptionKey, uint32) error {if .Payload == nil {returnerrors.New("payload has not been set") }if .CheckSum != nil {returnerrors.New("checksum has already been computed") } , := .computeCheckSum(, )if != nil {return } .CheckSum = returnnil}// ComputeCheckSum computes and returns the checksum of this token, computed using the passed key and key usage.// Note: This will NOT update the struct's Checksum field.func ( *WrapToken) ( types.EncryptionKey, uint32) ([]byte, error) {if .Payload == nil {returnnil, errors.New("cannot compute checksum with uninitialized payload") }// Build a slice containing { payload | header } := make([]byte, HdrLen+len(.Payload))copy([0:], .Payload)copy([len(.Payload):], getChecksumHeader(.Flags, .SndSeqNum)) , := crypto.GetEtype(.KeyType)if != nil {returnnil, }return .GetChecksumHash(.KeyValue, , )}// Build a header suitable for a checksum computationfunc getChecksumHeader( byte, uint64) []byte { := make([]byte, 16)copy([0:], []byte{0x05, 0x04, , 0xFF, 0x00, 0x00, 0x00, 0x00})binary.BigEndian.PutUint64([8:], )return}// Verify computes the token's checksum with the provided key and usage,// and compares it to the checksum present in the token.// In case of any failure, (false, Err) is returned, with Err an explanatory error.func ( *WrapToken) ( types.EncryptionKey, uint32) (bool, error) { , := .computeCheckSum(, )if != nil {returnfalse, }if !hmac.Equal(, .CheckSum) {returnfalse, fmt.Errorf("checksum mismatch. Computed: %s, Contained in token: %s",hex.EncodeToString(), hex.EncodeToString(.CheckSum)) }returntrue, nil}// Unmarshal bytes into the corresponding WrapToken.// If expectFromAcceptor is true, we expect the token to have been emitted by the gss acceptor,// and will check the according flag, returning an error if the token does not match the expectation.func ( *WrapToken) ( []byte, bool) error {// Check if we can read a whole headeriflen() < 16 {returnerrors.New("bytes shorter than header length") }// Is the Token ID correct?if !bytes.Equal(getGssWrapTokenId()[:], [0:2]) {returnfmt.Errorf("wrong Token ID. Expected %s, was %s",hex.EncodeToString(getGssWrapTokenId()[:]),hex.EncodeToString([0:2])) }// Check the acceptor flag := [2] := &0x01 == 1if && ! {returnerrors.New("unexpected acceptor flag is set: not expecting a token from the acceptor") }if ! && {returnerrors.New("expected acceptor flag is not set: expecting a token from the acceptor, not the initiator") }// Check the filler byteif [3] != FillerByte {returnfmt.Errorf("unexpected filler byte: expecting 0xFF, was %s ", hex.EncodeToString([3:4])) } := binary.BigEndian.Uint16([4:6])// Sanity check on the checksum lengthifint() > len()-HdrLen {returnfmt.Errorf("inconsistent checksum length: %d bytes to parse, checksum length is %d", len(), ) } .Flags = .EC = .RRC = binary.BigEndian.Uint16([6:8]) .SndSeqNum = binary.BigEndian.Uint64([8:16]) .Payload = [16 : len()-int()] .CheckSum = [len()-int():]returnnil}// NewInitiatorWrapToken builds a new initiator token (acceptor flag will be set to 0) and computes the authenticated checksum.// Other flags are set to 0, and the RRC and sequence number are initialized to 0.// Note that in certain circumstances you may need to provide a sequence number that has been defined earlier.// This is currently not supported.func ( []byte, types.EncryptionKey) (*WrapToken, error) { , := crypto.GetEtype(.KeyType)if != nil {returnnil, } := WrapToken{Flags: 0x00, // all zeroed out (this is a token sent by the initiator)// Checksum size: length of output of the HMAC function, in bytes.EC: uint16(.GetHMACBitLength() / 8),RRC: 0,SndSeqNum: 0,Payload: , }if := .SetCheckSum(, keyusage.GSSAPI_INITIATOR_SEAL); != nil {returnnil, }return &, nil}
The pages are generated with Goldsv0.6.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds.